package com.jfcom.directorymonitor.service;

import lombok.extern.slf4j.Slf4j;
import net.contentobjects.jnotify.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.SmartLifecycle;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Map;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;

@Component
@Slf4j
public class JNotifyFileWatcher implements SmartLifecycle, JNotifyListener {

    private final AtomicBoolean running = new AtomicBoolean(false);
    private volatile int watchId = -1; // 使用 volatile 保证可见性
    private final ExecutorService eventProcessor = Executors.newFixedThreadPool(4);
    private final BlockingQueue<Runnable> pendingEvents = new LinkedBlockingQueue<>(1000);

    @Value("${jnotify.watch-dir}")
    private String watchDir;

    @Value("${jnotify.mask}")
    private String mask;

    @Value("${jnotify.watch-subtree}")
    private boolean watchSubtree;

    // 核心位置：在 start() 方法中初始化监听
    @Override
    public void start() {
        if (running.compareAndSet(false, true)) {
            try {
                // 1. 验证目录存在且可访问
                validateDirectoryAccess();

                // 2. 解析事件掩码
                int eventMask = parseEventMask();

                // 3. 核心：添加 JNotify 监听 (关键位置)
                watchId = JNotify.addWatch(
                        watchDir,
                        eventMask,
                        watchSubtree,
                        this
                );

                log.info("JNotify 监听已启动 | 目录: {} | 事件: {} | 监听子目录: {}",
                        watchDir, mask, watchSubtree);

                // 4. 启动事件处理线程
                startEventProcessingThread();

            } catch (JNotifyException e) {
                handleJNotifyException(e);
            } catch (Exception e) {
                log.error("监听器启动失败", e);
                stop();
            }
        }
    }

    private void validateDirectoryAccess() {
        Path path = Paths.get(watchDir);
        if (!Files.exists(path)) {
            try {
                Files.createDirectories(path);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            //throw new IllegalStateException("目录不存在: " + watchDir);
        }
        if (!Files.isReadable(path)) {
            throw new IllegalStateException("目录不可读: " + watchDir);
        }
        if (!Files.isDirectory(path)) {
            throw new IllegalStateException("路径不是目录: " + watchDir);
        }
    }

    private int parseEventMask() {
        int maskValue = 0;
        for (String event : mask.split("\\|")) {
            switch (event.trim().toUpperCase()) {
                case "CREATE": maskValue |= JNotify.FILE_CREATED; break;
                case "MODIFY": maskValue |= JNotify.FILE_MODIFIED; break;
                case "DELETE": maskValue |= JNotify.FILE_DELETED; break;
                case "RENAME": maskValue |= JNotify.FILE_RENAMED; break;
                default:
                    log.warn("忽略未知事件类型: {}", event);
            }
        }
        return maskValue;
    }

    private void startEventProcessingThread() {
        new Thread(() -> {
            while (running.get()) {
                try {
                    Runnable task = pendingEvents.poll(1, TimeUnit.SECONDS);
                    if (task != null) {
                        eventProcessor.submit(task);
                    }

                    // 监控队列积压
                    if (pendingEvents.size() > 800) {
                        log.warn("事件队列积压: {} 个待处理事件", pendingEvents.size());
                    }
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        }, "JNotify-Event-Dispatcher").start();
    }

    // 事件回调方法 (JNotifyListener 实现)
    @Override
    public void fileCreated(int wd, String rootPath, String name) {
        enqueueEvent("CREATE", rootPath, name);
    }

    @Override
    public void fileModified(int wd, String rootPath, String name) {
        enqueueEvent("MODIFY", rootPath, name);
    }

    @Override
    public void fileDeleted(int wd, String rootPath, String name) {
        enqueueEvent("DELETE", rootPath, name);
    }

    @Override
    public void fileRenamed(int wd, String rootPath, String oldName, String newName) {
        enqueueRenameEvent(rootPath, oldName, newName);
    }

    private void enqueueEvent(String type, String root, String filename) {
        if (!running.get()) return;

        // 快速过滤系统文件
        if (filename.startsWith(".") || filename.endsWith("~")) return;

        pendingEvents.offer(() -> {
            try {
                Path fullPath = Paths.get(root, filename);
                handleFileEvent(type, fullPath.toString());
            } catch (Exception e) {
                log.error("处理事件失败 [{}] {}: {}", type, filename, e.getMessage());
            }
        });
    }

    private void handleFileEvent(String type, String string) {
        log.info("type: {} | fullPath: {}", type, string);
    }

    private void enqueueRenameEvent(String root, String oldName, String newName) {
        pendingEvents.offer(() -> {
            try {
                Path oldPath = Paths.get(root, oldName);
                Path newPath = Paths.get(root, newName);
                handleRenameEvent(oldPath.toString(), newPath.toString());
            } catch (Exception e) {
                log.error("处理重命名事件失败 {} -> {}: {}", oldName, newName, e.getMessage());
            }
        });
    }

    private void handleRenameEvent(String string, String string1) {
        log.info("oldName: {} | newName: {}", string, string1);
    }

    // 停止时释放资源
    @Override
    public void stop() {
        if (running.compareAndSet(true, false)) {
            try {
                // 1. 移除JNotify监听
                if (watchId != -1) {
                    JNotify.removeWatch(watchId);
                    log.info("JNotify监听已移除");
                }

                // 2. 关闭线程池
                eventProcessor.shutdown();
                if (!eventProcessor.awaitTermination(10, TimeUnit.SECONDS)) {
                    eventProcessor.shutdownNow();
                }

                // 3. 清空事件队列
                pendingEvents.clear();

            } catch (JNotifyException e) {
                log.error("移除监听失败", e);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } finally {
                watchId = -1;
            }
        }
    }

    // 异常处理
    private void handleJNotifyException(JNotifyException e) {
        switch (e.getErrorCode()) {
            case JNotifyException.ERROR_WATCH_LIMIT_REACHED:
                log.error("系统inotify监视数达到上限，请调整: {}", e.getMessage());
                break;
            case JNotifyException.ERROR_PERMISSION_DENIED:
                log.error("目录访问被拒绝: {}", watchDir);
                break;
            case JNotifyException.ERROR_NO_SUCH_FILE_OR_DIRECTORY:
                log.error("目录不存在: {}", watchDir);
                break;
            default:
                log.error("JNotify异常: {}", e.getMessage());
        }
        stop();
        throw new RuntimeException("JNotify初始化失败: " + e.getMessage(), e);
    }

    // 其他必要方法
    @Override public boolean isRunning() { return running.get(); }
    @Override public int getPhase() { return Integer.MAX_VALUE; } // 最后启动最先关闭

    // 监控端点
    public Map<String, Object> getStatus() {
        return Map.of(
                "active", running.get(),
                "watchId", watchId,
                "watchDir", watchDir,
                "pendingEvents", pendingEvents.size()
        );
    }

}